<?php

namespace app\home\worker;

use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\Table;
use Swoole\WebSocket\Frame;
use Swoole\WebSocket\Server;

class msgIm
{
    /** @type Server */
    public static $server;
    public static $serverListen;
    /** @type gTable */
    public static $numEvent;
    public static $row;

    public function start()
    {
        self::$serverListen = array();
        self::$numEvent = new gTable(10000, 4096);
        self::$server = new Server("0.0.0.0", 8080, SWOOLE_BASE, SWOOLE_SOCK_TCP);// | SWOOLE_SSL
        $setConfig = [
            'ssl_key_file' => '/ssl/app888.key',
            'ssl_cert_file' => '/ssl/app888.pem'
        ];
        // self::$server->set($setConfig);
        self::$server->on('open', function (Server $server, Request $request) {
            self::logs("server: open");
            self::logs("server: handshake success with fd->{$request->fd}");
        });
        self::$server->on('message', function (Server $server, Frame $frame) {
            $fd = $frame->fd;
            $json = json_decode($frame->data, true);
            if ($json['code'] > 99) {
                if (empty($json['code']) || empty($json['uid'])) {
                    @self::$server->push($fd, json_encode(["fd" => $fd, "code" => 101, "msg" => "cmd error!"]));
                } else {
                    // $uid = self::rc4(md5('fox' . date('Y-m-d', time())), base64_decode($json['uid']));
                    $uid = $json['uid'];
                    switch ($json['code']) {
                        case '100':
                            @self::$server->push($fd, json_encode(["fd" => $fd, "code" => 100, "msg" => "link " . $uid . " success"]));
                            self::$numEvent->add($uid, $fd);
                            self::$numEvent->add('userList', ["fd" => $fd, "uid" => $uid, "name" => $json['name'], "img" => $json['img']]);
                            $userList = self::$numEvent->get('userList');
                            foreach (self::$numEvent->all() as $key => $numItem) {
                                foreach ($numItem as $index => $item) {
                                    @self::$server->push($item, json_encode(["fd" => $fd, "code" => 101, "userList" => $userList]));
                                }
                            }
                            break;
                        default:
                            @self::$server->push($fd, json_encode(["fd" => $fd, "code" => 100, "msg" => "send ok"]));
                            foreach (self::$numEvent->get($uid) as $fdx) {
                                $json['fd'] = $fdx;
                                @self::$server->push($fdx, json_encode($json));
                            }
                            break;
                    }
                }
            } else {
                @self::$server->push($fd, json_encode(["fd" => $fd, "code" => 100, "msg" => "heartbeat"]));
            }
        });
        self::$server->on('close', function (Server $server, int $fd) {
            foreach (self::$numEvent->all() as $key => $numItem) {
                foreach ($numItem as $index => $item) {
                    if ($item == $fd) unset($numItem[$index]);
                }
                self::$numEvent->set($key, $numItem);
            }
            $userList = self::$numEvent->get('userList');
            foreach ($userList as $index => $item) {
                if ($item['fd'] == $fd) unset($userList[$index]);
            }
            self::$numEvent->set('userList', $userList);
            $userList = self::$numEvent->get('userList');
            foreach (self::$numEvent->all() as $key => $numItem) {
                foreach ($numItem as $index => $item) {
                    @self::$server->push($item, json_encode(["fd" => $fd, "code" => 101, "userList" => $userList]));
                }
            }
            self::logs("client {$fd} closed");
        });
        self::$server->on('request', function (Request $request, Response $response) {
            $response->header('Content-type', 'application/json');
            $json = $request->get;
            if (empty($json['code']) || empty($json['uid'])) {
                $response->end(json_encode(["res" => 0, "msg" => "cmd error"]));
            } else {
                $isSend = 0;
                /*$jobnum = rc4(md5('fox' . date('Y-m-d', time())), base64_decode($json['uid']));
                foreach (self::$numEvent->get($jobnum) as $fdx) {
                    $json['fd'] = $fdx;
                    @self::$server->push($fdx, json_encode($json));
                    $isSend++;
                }*/
                if ($isSend > 0)
                    $response->end(json_encode(["res" => 1, "msg" => "消息发送成功", "isSend" => $isSend]));
                else
                    $response->end(json_encode(["res" => 0, "msg" => "对方不在线，请稍后发送", "isSend" => $isSend]));
            }
        });
        self::logs("WebSocket Init! ");
        self::$server->start();
    }

    public static function logs(... $args)
    {
        $numArgs = func_num_args();
        $arg_list = func_get_args();
        $str = " ";
        for ($i = 0; $i < $numArgs; $i++) {
            $str .= print_r($arg_list[$i], true) . "\n";
        }
        echo "【" . date('Y-m-d H:i:s') . "】$str";
    }

    /**
     * rc4加密算法,解密方法直接再一次加密就是解密
     * @param  $pwd  加密使用的key
     * @param  $data 要加密的数据
     * @return string [description]
     */
    function rc4($pwd, $data)
    {
        $cipher = '';
        $key[] = "";
        $box[] = "";
        $pwd_length = strlen($pwd);
        $data_length = strlen($data);
        for ($i = 0; $i < 256; $i++) {
            $key[$i] = ord($pwd[$i % $pwd_length]);
            $box[$i] = $i;
        }
        for ($j = $i = 0; $i < 256; $i++) {
            $j = ($j + $box[$i] + $key[$i]) % 256;
            $tmp = $box[$i];
            $box[$i] = $box[$j];
            $box[$j] = $tmp;
        }
        for ($a = $j = $i = 0; $i < $data_length; $i++) {
            $a = ($a + 1) % 256;
            $j = ($j + $box[$a]) % 256;
            $tmp = $box[$a];
            $box[$a] = $box[$j];
            $box[$j] = $tmp;
            $k = $box[(($box[$a] + $box[$j]) % 256)];
            $cipher .= chr(ord($data[$i]) ^ $k);
        }
        return $cipher;
    }
}

class gTable
{

    private $mem;

    public function __construct($len = 1000, $size = 1024)
    {
        $this->mem = new Table($len);
        $this->mem->column('val', Table::TYPE_STRING, $size);
        $this->mem->create();
    }

    function set($key, $val)
    {
        $str = json_encode($val); //如果数据超出  分多个存储，然后这边保留值即可
        $this->mem->set($key, array('val' => $str));
    }

    function get($key)
    {
        $row = $this->mem->get($key);
        if (!$row) return array();
        //如果是组合值  就提取多个key值进行拼凑
        $json = json_decode($row['val'], true);
        return $json;
    }

    function del($key)
    {
        return $this->mem->del($key);
    }

    function each($func)
    {
        foreach ($this->mem as $key => $row) {
            $val = json_decode($row['val'], true);
            $func($key, $val);
        }
    }

    function count()
    {
        return count($this->mem);
    }

    function has($key)
    {
        return $this->mem->exist($key);
    }

    function keys()
    {
        $ret = array();
        foreach ($this->mem as $key => $row) {
            $ret[] = $key;
        }
        return $ret;
    }

    function all()
    {
        $ret = array();
        foreach ($this->mem as $key => $row) {
            $ret[$key] = json_decode($row['val'], true);
        }
        return $ret;
    }

    function add($key, $val, $maxcnt = 100)
    {
        $row = $this->mem->get($key);
        if (!$row) {
            $this->mem->set($key, ['val' => json_encode([$val])]);
            return 1;
        }
        //如果是组合值  就提取多个key值进行拼凑
        $json = json_decode($row['val'], true);
        array_push($json, $val);
        if (count($json) > $maxcnt) {
            $json = array_pop($json);
        }
        $this->mem->set($key, ['val' => json_encode($json)]);
        return count($json);
    }

}

$im = new msgIm();
$im->start();